package com.seu.githubcn.controller;

import com.seu.githubcn.config.Result;
import com.seu.githubcn.entity.RepoInfo;
import com.seu.githubcn.utils.Zip;
import lombok.Data;
import org.eclipse.jgit.api.Git;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static com.seu.githubcn.utils.Utils.*;


@RestController
@RequestMapping()
@Slf4j
public class FolderController {

    @Value("${githubcn.base-path}")
    String BASE;
    @Value("${githubcn.shell}")
    String SHELL;
    @Value("${githubcn.cloc}")
    String CLOC;

    @GetMapping("{userName}/{repoName}/tree/{branch}/**")
    public Result<?> dict(HttpServletRequest request,
                          @PathVariable("userName") String userName,
                          @PathVariable("repoName") String repoName) {
        String path = request.getServletPath();
        var urlSlides = path.split("/");
        RepoInfo repoInfo = parseUrl(urlSlides,"tree");
        Git git = openRepo(repoInfo.getBasePath());

        if(!checkBranch(git,repoInfo.getBranch())){
            return new Result<>(404, null, "检查分支失败！");
        }
        var base = repoInfo.getBasePath();
        if(base.charAt(base.length() - 1)=='/'){
            base = base.substring(0,base.length() - 1);
        }


        var ls = "cd / ; cd %s ; git ls-tree %s %s"
                .formatted(base,repoInfo.getBranch(),repoInfo.getExtendPath());
        List<FileInfo> fileInfoList = null;
        try {
            Method parseList = getClass().getDeclaredMethod("parseList", BufferedReader.class, Process.class);
            var content = execShell(ls,parseList);
            if(content==null){
                return new Result<>(404, null, "命令执行失败！");
            }
            fileInfoList= (List<FileInfo>) content;
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return new Result<>(200, fileInfoList, "查询成功!");

    }


    @GetMapping("{userName}/{repoName}/blob/{branch}/**")
    public Result<?> preview(HttpServletRequest request,
                             @PathVariable("userName") String userName,
                             @PathVariable("repoName") String repoName) {
        String path = request.getServletPath();
        var urlSlides = path.split("/");
        RepoInfo repoInfo = parseUrl(urlSlides,"blob");
        Git git = openRepo(repoInfo.getBasePath());

        if(!checkBranch(git,repoInfo.getBranch())){
            return new Result<>(404, null, "检查分支失败！");
        }
        var base = repoInfo.getBasePath();
        if(base.charAt(base.length() - 1)=='/'){
            base = base.substring(0,base.length() - 1);
        }


        try{
            Method parseSha = getClass().getDeclaredMethod("parseSha", BufferedReader.class, Process.class);
            Method parseCat = getClass().getDeclaredMethod("parseCat", BufferedReader.class, Process.class);
            var shellSha ="cd / ; cd %s ; git ls-tree %s %s"
                    .formatted(base,repoInfo.getBranch(),
                    repoInfo.getExtendPath().substring(0,repoInfo.getExtendPath().length()-1));

            var sha = execShell(shellSha,parseSha);
            if(sha==null){
                return new Result<>(404, null, "命令执行失败！");
            }
            var shellCat ="cd / ; cd %s ; git cat-file -p %s"
                    .formatted(repoInfo.getBasePath(),sha.toString());
            var content = execShell(shellCat,parseCat);
            if(content==null){
                return new Result<>(404, null, "命令执行失败！");
            }
            return new Result<>(200,content.toString(),"文件传输成功！");
        }catch (Exception e){
            e.printStackTrace();
        }
        return new Result<>(404, null, "命令执行失败！");

    }

    @GetMapping("{userName}/{repoName}/download/{branch}/**")
    public void download(HttpServletRequest request, HttpServletResponse response) {
        String path = request.getServletPath();
        var urlSlides = path.split("/");
        RepoInfo repoInfo = parseUrl(urlSlides,"download");
        Git git = openRepo(repoInfo.getBasePath());

        if(!checkBranch(git,repoInfo.getBranch())){
            if(!checkTag(git, repoInfo.getBranch())){
                return;
            }
        }

        var tmpPath = repoInfo.getTmpPath();
        if(tmpPath.charAt(tmpPath.length() - 1)=='/'){
            tmpPath = tmpPath.substring(0,tmpPath.length() - 1);
        }
        try {
            var shellOrigin = "cd / ; cd %s ; git checkout origin/%s"
                    .formatted(tmpPath,repoInfo.getBranch());

            execShell(shellOrigin,null);
            File tmp = new File(tmpPath);
            Zip zip =new Zip();
            File zipFile = zip.zipCompress(tmp,false);
            downloadFile(response,zipFile);
            log.info("zip 删除情况：" + zipFile.delete());

        } catch (Exception ignored) {
        }
    }

    @GetMapping("{userName}/{repoName}/code/{branch}/**")
    public Result<?> code(HttpServletRequest request, HttpServletResponse response) {
        String path = request.getServletPath();
        var urlSlides = path.split("/");
        RepoInfo repoInfo = parseUrl(urlSlides,"code");
        Git git = openRepo(repoInfo.getBasePath());

        if(!checkBranch(git,repoInfo.getBranch())){
            return new Result<>(400,null,"无分支！");
        }
        var tmpPath = repoInfo.getTmpPath();
        if(tmpPath.charAt(tmpPath.length() - 1)=='/'){
            tmpPath = tmpPath.substring(0,tmpPath.length() - 1);
        }
        try {
            var shellOrigin = "cd %s ; git checkout origin/%s ; git pull"
                    .formatted(tmpPath,repoInfo.getBranch());
            System.out.println(shellOrigin);
            execShell(shellOrigin);

            var shellCloc = "%s %s ".formatted(CLOC,tmpPath);
            System.out.println(shellCloc);
            Method parseCode = getClass().getMethod("parseCode", BufferedReader.class, Process.class);
            var content = execShell(shellCloc,parseCode);

            if(content==null){
                return new Result<>(400,null,"命令执行失败！");
            }
            return new Result<>(200,(List<CodeInfo>)content,"发送成功！");
        } catch (Exception e) {
            return new Result<>(400,null,"命令执行失败！");
        }
    }


    public void downloadFile(HttpServletResponse response, File file){
        if(!file.exists()){
            return;
        }
        response.reset();
        response.setContentType("application/octet-stream");
        response.setCharacterEncoding("utf-8");
        response.setContentLength((int) file.length());
        response.setHeader("Content-Disposition", "attachment;filename=" + file.getName() );

        try{
            FileInputStream fis = new FileInputStream(file);
            BufferedInputStream bis = new BufferedInputStream(fis);
            byte[] buff = new byte[1024];
            OutputStream os  = response.getOutputStream();
            int i;
            while ((i = bis.read(buff)) != -1) {
                os.write(buff, 0, i);
                os.flush();
            }
            os.close();
            bis.close();
            fis.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }


    /**
     *
     * @param shell 要执行的shell语句
     * @param parse 对shell返回值做解析
     * @return 返回解析得到的对象
     */
    public Object execShell(String shell, Method parse){
        Runtime run =Runtime.getRuntime();
        InputStream is =null;
        BufferedReader reader = null;
        try {
            Process p = run.exec(new String[]{SHELL,shell});
            is = p.getInputStream();
            reader = new BufferedReader(new InputStreamReader(is));
            if(parse == null)return null;
           return parse.invoke(this,reader,p);

        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try{
                if(reader!=null)reader.close();
                if(is!=null)is.close();
            }catch (Exception ignored){}
        }
        return null;
    }

    public void execShell(String shell){
        try {
            Runtime.getRuntime().exec(new String[]{SHELL,shell});
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public List<CodeInfo> parseCode(BufferedReader reader,Process p)throws Exception{
        List<CodeInfo> codeInfoList = new ArrayList<>();

        String s;
        while ((s = reader.readLine()) != null) {
            System.out.println(s);
            if(s.equals(""))continue;
            if (s.charAt(0) == '-'){
                break;
            }
            p.waitFor();
        }
        while ((s = reader.readLine()) != null) {
            System.out.println(s);
            if(s.equals(""))continue;
            if (s.charAt(0) == '-'){

                continue;

            }

            CodeInfo codeInfo = new CodeInfo();
            var slides = s.split("\\s{2,}");
            if(slides.length<5)continue;
            System.out.println(Arrays.toString(slides));
            codeInfo.setLanguage(slides[0]);
            codeInfo.setFiles(slides[1]);
            codeInfo.setBlank(slides[2]);
            codeInfo.setComment(slides[3]);
            codeInfo.setCode(slides[4]);
            codeInfoList.add(codeInfo);
            p.waitFor();
        }
        if(codeInfoList.size()!=0) codeInfoList.remove(0);
        return codeInfoList;
    }

    public String parseCat(BufferedReader reader,Process p)throws Exception{
        StringBuilder res = new StringBuilder();
        String s;
        while ((s= reader.readLine())!=null){
            res.append(s);
            res.append('\n');
            p.waitFor();
        }
        return res.toString();
    }

    public String parseSha(BufferedReader reader,Process p)throws Exception{
        var s = reader.readLine();

        if (s != null) {
            var content = s.split("\t")[0];
            return content.split(" ")[2];
        }else{
            return null;
        }

    }

    public Object parseList(BufferedReader reader,Process p)throws Exception{
        List<FileInfo> fileInfoList = new ArrayList<>();

        String s;
        while ((s = reader.readLine()) != null) {
            var kind = s.split(" ")[1];
            var _name = s.split("\t")[1];
            var name = _name.substring(_name.lastIndexOf("/")+1);
            FileInfo fileInfo = new FileInfo();
            fileInfo.setFolderName(name);
            fileInfo.setIsDirectory(kind.equals("tree"));
            fileInfoList.add(fileInfo);
            p.waitFor();
        }
        return fileInfoList;
    }

    public RepoInfo parseUrl(String[] urlSlides,String keyWord){
        int i;
        for (i = 0; i < urlSlides.length; i++) {
            if (urlSlides[i].equals(keyWord)) {
                break;
            }
        }
        var userName = urlSlides[i - 2];
        var repoName = urlSlides[i - 1];
        var branch = urlSlides[i + 1];
        i += 2;
        var basePath = BASE.formatted(userName, repoName) + ".git/";
        var tmpPath = "/tmp%s".formatted(BASE.formatted(userName, repoName));
        var localPath = constructLocalPath(BASE.formatted(userName, repoName),urlSlides,i);
        var extendPath = constructLocalPath("",urlSlides,i);
        RepoInfo repoInfo =new RepoInfo();
        repoInfo.setTmpPath(tmpPath);
        repoInfo.setUserName(userName);
        repoInfo.setRepoName(repoName);
        repoInfo.setBranch(branch);
        repoInfo.setBasePath(basePath);
        repoInfo.setLocalPath(localPath);
        repoInfo.setExtendPath(extendPath);
        return repoInfo;
    }

    @PostMapping("{userName}/{repoName}/upload/{branch}/**")
    public Result<?> upload(HttpServletRequest request) {
        String path = request.getServletPath();
        var urlSlides = path.split("/");
        RepoInfo repoInfo = parseUrl(urlSlides,"upload");
        Git git = openRepo(repoInfo.getBasePath());

        if(!checkBranch(git,repoInfo.getBranch())){
            return new Result<>(404, null, "检查分支失败！");
        }
        var tmpPath = "/tmp%s".formatted(repoInfo.getBasePath());
        var tmpLocalPath = "/tmp%s".formatted(repoInfo.getLocalPath());
        var random = getRandomString(10);
        try {
            var shellOrigin = "cd %s | git checkout -b %s origin/%s"
                    .formatted(tmpPath,random,repoInfo.getBranch());
            execShell(shellOrigin,null);
            File folder = new File(tmpLocalPath);
            if(!folder.isDirectory()){
                return new Result<>(404,null,"访问文件非目录！");
            }

            if(!uploadFile(request, tmpLocalPath)){
                return new Result<>(404,null,"上传失败！");
            }
            var shellAdd = "cd %s | git add . | commit -m \"add file\" | git push origin/%s"
                    .formatted(tmpPath,repoInfo.getBranch());
            execShell(shellAdd);
            System.gc();//垃圾回收，解除占用
            var shellIgnore ="cd %s | git checkout ignore".formatted(tmpPath);
            var shellDelete="cd %s | git branch -D %s".formatted(tmpPath,random);
            execShell(shellIgnore);
            execShell(shellDelete);

        } catch (Exception e) {
            return new Result<>(404,null,"上传失败！");
        }


        return new Result<>(200, null, "查询成功!");



    }

    public Boolean uploadFile(HttpServletRequest request,String filePath) {

        ServletInputStream sis = null;
        FileOutputStream fos = null;
        try {
            // 文件名
            String filename = request.getHeader("fileName");
            // 文件类型，例如：jpg、png、pdf...
            String fileType = request.getHeader("fileType");

            var loadPath = filePath+filename+"."+fileType;
            File file = new File(loadPath);
            if(!file.getParentFile().exists()) {
                return false;
            }
            if(!file.exists()) {
                if(!file.createNewFile()){
                    return false;
                }
            }
            sis = request.getInputStream();
            fos = new FileOutputStream(file);
            byte[] content = new byte[1024];
            int len;
            while((len=sis.read(content)) > -1) {
                fos.write(content, 0, len);
            }
            fos.flush();

            return true;
        } catch (Exception ex) {
            return false;
        } finally {
            try {
                if(sis!=null) {
                    sis.close();
                }
                if(fos!=null) {
                    fos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    @Data
    private static class FileInfo {
        private String folderName;
        private Boolean isDirectory;
    }

    @Data
    private static class CodeInfo{
        private String Language;
        private String files;
        private String blank;
        private String comment;
        private String code;
    }

//    private static void downloadFile(HttpServletResponse response,File file){
//        response.setContentType("application/force-download");
//        response.addHeader("Content-Disposition", "attachment;fileName=" + file.getName());
//        byte[] buffer = new byte[1024];
//        try {
//            FileInputStream fis = new FileInputStream(file);
//            BufferedInputStream bis = new BufferedInputStream(fis);
//            OutputStream os = response.getOutputStream();
//
//            int size = bis.read(buffer);
//            while (size != -1) {
//                os.write(buffer, 0, size);
//                size = bis.read(buffer);
//            }
//        }catch (Exception ignored){
//
//        }
//
//    }

    //    //将File转化成String
//    private static String fileToString(String filePath) {
//        //对一串字符进行操作
//        StringBuilder fileData = new StringBuilder();
//        //
//        BufferedReader reader;
//        try {
//            reader = new BufferedReader(new FileReader(filePath));
//
//        char[] buf = new char[1024];
//        int numRead;
//        while ((numRead = reader.read(buf)) != -1) {
//            String readData = String.valueOf(buf, 0, numRead);
//            fileData.append(readData);
//        }
//        //缓冲区使用完必须关掉
//        reader.close();
//        System.out.println(fileData);
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//        return fileData.toString();
//    }



    //    private static Boolean loadFile(File[] fileList,List<FileInfo> fileInfoList){
//        //文件转换
//        if (fileList == null) {
//            return false;
//        }
//        for (var file : fileList) {
//            FileInfo f = new FileInfo();
//            f.setFolderName(file.getName());
//            f.setIsDirectory(file.isDirectory());
//            fileInfoList.add(f);
//        }
//        return true;
//    }






}


